package com.atomos.system
{
	import com.atomos.particle.Particle;
	import com.atomos.particle.ParticleManager;
	import com.atomos.strategy.emission.EmissionStrategy;
	
	import com.bourre.collection.Iterator;
	import com.bourre.collection.WeakCollection;
	import com.bourre.error.NullPointerException;
	
	import com.kairos.engine.RTObject;
	
	import flash.events.Event;

	/**
	 * <code>AbstractParticleSystem</code> provides a basic implementation for
	 * generic particle systems. It extends the <code>RTObject</code> and 
	 * implements real time behaviors for generation of particles. IT can store
	 * several <code>EmissionStrategy</code> but don't implements initializations
	 * and actions strategies storage.
	 * 
	 * <p>When extending the <code>AbstractParticleSystem</code> class you will
	 * override the <code>_initializeParticle</code>, <code>_prepareAction</code>
	 * and <code>_processParticle</code> protected methods in order to create the
	 * concret implementation of those behaviors.</p>
	 * 
	 * @author Cédric Néhémie
	 * @see	   ParticleSystem
	 * @see	   com.kairos.engine.RTObject
	 */
	public class AbstractParticleSystem extends RTObject implements ParticleSystem
	{
		protected var _cParticles : WeakCollection;
		protected var _aEmissions : Array;
		
		public function AbstractParticleSystem () 
		{
			ParticleManager.getInstance().addParticleSystem( this );
			_cParticles = new WeakCollection ();
			_aEmissions = new Array();
		}
		
		public function emit ( emission : EmissionStrategy = null ) : void
		{
			if( emission == null )
				throw new NullPointerException( this + ".emit was called with a null emission" );

			_aEmissions.push( emission );	
			_startEmission( emission );
			
			start();
		}
		
		public function isEmitting() : Boolean
		{
			return _aEmissions.length != 0;
		}
		
		public function setParticles ( particles : WeakCollection ) : void
		{
			_cParticles = particles;
		}
		
		public function getParticles() : WeakCollection
		{
			return _cParticles;
		}
		public function getParticlesCount() : Number
		{
			return _cParticles.size();
		}
		
		public function getEmissions () : WeakCollection
		{
			return new WeakCollection ( _aEmissions );
		}
		
		public function getEmissionsCount () : Number
		{
			return _aEmissions.length;
		}
		
		public function release () : void
		{
			ParticleManager.getInstance().removeParticleSystem( this );
		}
		
		public override function onTick( e : Event = null ) : void
		{
			_processParticles( e );
			
			if ( isEmitting() )
			{
				_processEmissions( e );
			}
		}
		
		/*-------------------------------------------------------
				PROTECTED MEMBERS
		---------------------------------------------------------*/
		
		protected function _startEmission ( emission : EmissionStrategy ) : void
		{
			while( emission.hasNext() )
			{
				var time : Number = emission.nextTime(); 
				var particle: Particle = emission.next() as Particle;
				
				_registerParticle( particle );					
				_initializeParticle( particle, time );
			}
			
			if( emission.isFinish() ) 
			{
				_removeEmission( emission );
			}
		}
		
		protected function _processEmissions ( e : Event ) : void
		{
			var l : Number = _aEmissions.length;
			var t : Number = getLocalStep( e );
			
			while ( l-- )
			{
				var emission : EmissionStrategy = _aEmissions[ l ] as EmissionStrategy;
				
				emission.prepareEmission( t );
				
				while( emission.hasNext() )
				{
					var time : Number = emission.nextTime(); 
					var particle : Particle = emission.next() as Particle;
					
					_registerParticle( particle );
					_initializeParticle( particle, time );
				}
				if( emission.isFinish() ) 
				{
					_removeEmission( emission );
				}
			}
		}
		
		protected function _removeEmission ( emission : EmissionStrategy ) : void
		{
			var i : Number = _aEmissions.indexOf( emission );
			_aEmissions.splice( i, 1 );
		}
		
		protected function _processParticles ( e : Event ) : void
		{
			var time : Number = getLocalStep( e );
			var i : Iterator = _cParticles.iterator();
			
			_prepareAction ( time );
			while ( i.hasNext() )
			{
				var particle : Particle = i.next() as Particle;
				
				_processParticle( particle );
				
				if( particle.life >= particle.maxLife )
				{
					_unregisterParticle( particle );
				}
			}
		}
				
		protected function _initializeParticle ( particle : Particle, time : Number ) : void	{}
		
		protected function _prepareAction ( time : Number ) : void {}
		
		protected function _processParticle ( particle : Particle ) : void {}
	
		protected function _registerParticle ( particle : Particle ) : void
		{			
			ParticleManager.getInstance().addParticle( particle );
			_cParticles.add( particle );
		}
		protected function _unregisterParticle ( particle : Particle ) : void
		{			
			ParticleManager.getInstance().removeParticle( particle );
			_cParticles.remove( particle );
		}
	}
}